package main

import (
	"errors"
	"net/rpc"

	"log"

	"flag"

	"strings"

	"learngo/crawler/config"
	"learngo/crawler/engine"
	"learngo/crawler/scheduler"
	"learngo/crawler/xcar/parser"
	itemsaver "learngo/crawler_distributed/persist/client"
	"learngo/crawler_distributed/rpcsupport"
	worker "learngo/crawler_distributed/worker/client"
)

var (
	itemSaverHost = flag.String(
		"itemsaver_host", "", "itemsaver host")

	workerHosts = flag.String(
		"worker_hosts", "",
		"worker hosts (comma separated)")
)

func main() {
	//屏幕读入
	flag.Parse()

	//客户端：连接服务端，从chan拉取item
	//这个chan,会在调用Run方法的的时候创建，item也是那个时候放入chan
	itemChan, err := itemsaver.ItemSaver(*itemSaverHost)
	if err != nil {
		panic(err)
	}

	//pool是客户端的chan
	pool, err := createClientPool(
		strings.Split(*workerHosts, ","))
	if err != nil {
		panic(err)
	}

	//调用远程，返回结果ParserResult
	//不管CreateProcessor目的是什么，只管它返回一个engine.Processor接口的实现定义
	//而这个engine.Processor作为ConcurrentEngine(engine/concurrent)的一个变量
	//ConcurrentEngine使用engine.Processor这个变量的时候，因为engine.Processor
	// 等价于type Processor func(Request) (ParserResult, error)
	//所以ConcurrentEngine使用这个变量就等价于使用了func(Request) (ParserResult, error)
	//这时候才有真正的Request参数参与运算
	//而远程调用解析，就是在上面的接口的实现定义中完成
	processor := worker.CreateProcessor(pool)

	e := engine.ConcurrentEngine{
		Scheduler:        &scheduler.QueuedScheduler{},
		WorkerCount:      100,
		ItemChan:         itemChan,
		RequestProcessor: processor,
	}

	//这个封装了个NewFuncParser，它返回了一个FuncParser的地址
	//因而Request.Parser返回的是FuncParser的地址
	//又因为FuncParser提供了连个方法Parse方法和Serialize方法
	//Request.Parser.Serialize,在默认实现中(engin\types),它返回的是FuncParser.name
	//对应是NewFuncParser的name,即config.ParseCarList
	//Request.Parser.Serialize,在特别实现中(zhenai\profile),它返回的是FuncParser.name外
	//还会返回用户名作为函数名为FuncParser.name的函数的参数
	//NewFuncParser和Request中的参数Parser的关系
	//Request中的参数Parser是个接口，NewFuncParser实现了这个接口的所有方法
	//所以NewFuncParser是Parser接口的具体实现
	e.Run(engine.Request{
		Url: "http://newcar.xcar.com.cn",
		Parser: engine.NewFuncParser(
			parser.ParseCarList,
			config.ParseCarList),
	})
}

func createClientPool(hosts []string) (chan *rpc.Client, error) {
	var clients []*rpc.Client
	for _, h := range hosts {
		client, err := rpcsupport.NewClient(h)
		if err == nil {
			clients = append(clients, client)
			log.Printf("Connected to %s", h)
		} else {
			log.Printf(
				"Error connecting to %s: %v",
				h, err)
		}
	}

	if len(clients) == 0 {
		return nil, errors.New(
			"no connections available")
	}
	out := make(chan *rpc.Client)
	go func() {
		for {
			for _, client := range clients {
				out <- client
			}
		}
	}()
	return out, nil
}
